﻿namespace UCSD_BioLit
{
    #region Namespace

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Text.RegularExpressions;
    using System.Windows.Forms;
    using Microsoft.Office.Core;
    using UCSD_BioLit.Properties;
    using MSWord = Microsoft.Office.Interop.Word;

    #endregion

    /// <summary>
    /// This class handles Ontology term recognition and creation of Ontology mark-up buttons for the Word context menu.
    /// </summary>
    public class OntologyTerm : OntologyTermBase
    {
        /// <summary>
        /// Class name for exception title.
        /// </summary>
        private const string ExceptionString = "OntologyTerm";

        /// <summary>
        /// Instance of the class OntologyTerm.
        /// </summary>
        private static OntologyTerm _ontologyTerm;

        /// <summary>
        /// The terms selected by the user, if it is not recognized.
        /// </summary>
        private string _defaultTerm = String.Empty;

        /// <summary>
        /// Range of the current user selection.
        /// </summary>
        private MSWord.Range _defaultTermRange;

        /// <summary>
        /// Id of the current selection.
        /// </summary>
        private string _defaultTermId = String.Empty;

        /// <summary>
        /// Gets or sets the ID of the current selection.
        /// </summary>
        public string DefaultTermId
        {
            get
            {
                return this._defaultTermId;
            }

            set
            {
                this._defaultTermId = value;
            }
        }

        /// <summary>
        /// Gets or sets the current selection text.
        /// </summary>
        public string DefaultTerm
        {
            get
            {
                return this._defaultTerm;
            }

            set
            {
                this._defaultTerm = value;
            }
        }

        /// <summary>
        /// Gets or sets the Range of the current selection.
        /// </summary>
        public MSWord.Range DefaultTermRange
        {
            get
            {
                return this._defaultTermRange;
            }

            set
            {
                this._defaultTermRange = value;
            }
        }

        /// <summary>
        /// Prevents a default instance of the OntologyTerm class from being created.
        /// </summary>
        private OntologyTerm()
            : base()
        {
        }

        /// <summary>
        /// Gets an Instance of the class OntologyTerm.
        /// </summary>
        /// <returns>The OntologyTerm instance.</returns>
        public static OntologyTerm Instance()
        {
            if (_ontologyTerm == null)
            {
                _ontologyTerm = new OntologyTerm();
            }

            return _ontologyTerm;
        }

        /// <summary>
        /// Overridden Recognize method to recognize Ontology Terms.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">TermRecognitionEventArgs object with details about the current selection and cursor position.</param>
        public override void Recognize(object sender, TermRecognitionEventArgs e)
        {
            this._defaultTerm = String.Empty;
            this._defaultTermId = String.Empty;
            this._defaultTermRange = null;

            this._termCollection.Clear();

            if (e != null)
            {
                string selectedText = e.SelectedText.Trim();

                if (!String.IsNullOrEmpty(selectedText))
                {
                    foreach (OntologyCache.OntologyInfo ontologyInfo in Globals.ThisAddIn.m_Ontologies)
                    {
                        // Search each term in the downloaded Ontologies
                        foreach (string term in ontologyInfo.m_TermLookup.Keys)
                        {
                            if (selectedText.IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1)
                            {
                                MSWord.Range range = e.SelectionRange.Duplicate;

                                // Find the range of all occurances of the terms near the current cursor position and add them to the list.
                                AddTermsInRange(range, term, e.CursorPosition, e.IsSelection);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Overridden CreateControls method to create Ontology mark-up controls on the Word context menus.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The EventArgs.</param>
        public override void CreateControls(object sender, EventArgs e)
        {
            if (TermCollection.Count > 0 || !String.IsNullOrEmpty(this._defaultTerm))
            {
                foreach (string contextMenuName in ContextMenuHelper.OntologyContextMenus)
                {
                    CommandBar contextMenu = Globals.ThisAddIn.Application.CommandBars[contextMenuName.ToString()];

                    if (contextMenu != null)
                    {
                        // Find the "Ontology - Mark-up Selection" popup control which has already been added to the context menu.
                        CommandBarPopup popupControl = (CommandBarPopup)contextMenu.FindControl(
                            MsoControlType.msoControlPopup, Type.Missing, ContextMenuHelper.ContextMenuTag, true, true);

                        this.AddDefaultTerm(popupControl, contextMenu.Id.ToString());
                        this.AddOntologyControl(popupControl, contextMenu.Id.ToString());
                    }
                }
            }
        }

        /// <summary>
        /// If the current selection is not a recognized Ontology term, it can be marked as an Ontology by the user.
        /// Hence, it is added to the context menu options by default.
        /// </summary>
        /// <param name="popupControl">The Popup control to add the Ontology mark-up button to.</param>
        /// <param name="contextMenuId">The ID of the Word context menu to which the control is being added.</param>
        private void AddDefaultTerm(CommandBarPopup popupControl, string contextMenuId)
        {
            if (!String.IsNullOrEmpty(this._defaultTerm))
            {
                try
                {
                    CommandBarButton buttonControl = (CommandBarButton)popupControl.Controls.Add(
                        MsoControlType.msoControlButton, System.Type.Missing, System.Type.Missing, System.Type.Missing, true);

                    buttonControl.Caption = this._defaultTerm;
                    buttonControl.Tag = this._defaultTermId + this.Separator + contextMenuId;
                    buttonControl.Click += this.OnOntologyButtonClick;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message, ExceptionString);
                }
            }
        }

        /// <summary>
        /// Adds the recognized Ontology mark-up control.
        /// </summary>
        /// <param name="popupControl">The Context Menu Popup control to add the Ontology Popup to.</param>
        /// <param name="contextMenuId">The ID of the Word context menu to which the control is being added.</param>
        private void AddOntologyControl(CommandBarPopup popupControl, string contextMenuId)
        {
            foreach (var term in TermCollection)
            {
                try
                {
                    CommandBarPopup ontologyPopup = (CommandBarPopup)popupControl.Controls.Add(
                                    MsoControlType.msoControlPopup, System.Type.Missing, System.Type.Missing, System.Type.Missing, true);
                    ontologyPopup.Caption = term.Value.RecognizedTerm;

                    this.AddOntologyControlOptions(ontologyPopup, contextMenuId, term);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message, ExceptionString);
                }
            }
        }

        /// <summary>
        /// Adds the Ontology mark-up options to the recognized term.
        /// </summary>
        /// <param name="popupControl">The recognized term Popup to which Ontology options need to be added.</param>
        /// <param name="contextMenuId">The ID of the Word context menu to which the control is being added.</param>
        /// <param name="term">The term that needs to be added.</param>
        private void AddOntologyControlOptions(CommandBarPopup popupControl, string contextMenuId, KeyValuePair<string, RecognizedTermProperties> term)
        {
            // Button to view the recognized term in the Ontology Browser.
            CommandBarButton showOntologyBrowserButton = (CommandBarButton)popupControl.Controls.Add(
                    MsoControlType.msoControlButton, System.Type.Missing, System.Type.Missing, System.Type.Missing, true);
            showOntologyBrowserButton.Caption = Resources.ViewInOntologyBrowserString;
            showOntologyBrowserButton.Tag = term.Key + Separator + contextMenuId + Separator + OntologyBrowserTagId.ToString();
            showOntologyBrowserButton.Click += this.OnOntologyButtonClick;

            // Button to apply the same mark-up to all recognized terms.
            CommandBarButton markAllInstancesButton = (CommandBarButton)popupControl.Controls.Add(
                    MsoControlType.msoControlButton, System.Type.Missing, System.Type.Missing, System.Type.Missing, true);
            markAllInstancesButton.Caption = Resources.ApplyMarkupToAllString;
            markAllInstancesButton.Tag = term.Key + Separator + contextMenuId + Separator + MarkAllInstancesTagId.ToString();
            markAllInstancesButton.Click += this.OnOntologyButtonClick;

            // Button to add mark-up only to the recognized term.
            string[] termCaptions = OntologyUtils.GetActionCaptions(term.Value.RecognizedTerm);
            if (termCaptions != null && termCaptions.Length > 0)
            {
                int tagId = 0;
                foreach (var termCaption in termCaptions)
                {
                    string buttonCaption = termCaption;
                    CommandBarButton buttonControl = (CommandBarButton)popupControl.Controls.Add(
                    MsoControlType.msoControlButton, System.Type.Missing, System.Type.Missing, System.Type.Missing, true);

                    // Button caption can only hold a maximum of 259 characters.
                    if (termCaption.Length > 250)
                    {
                        buttonCaption = termCaption.Substring(0, 250) + "...";
                    }

                    buttonControl.Caption = buttonCaption;
                    buttonControl.Tag = term.Key + Separator + contextMenuId + Separator + tagId.ToString();
                    buttonControl.Click += this.OnOntologyButtonClick;

                    tagId++;
                }
            }
        }

        /// <summary>
        /// Invoked when user clicks on the Ontology Mark-up buttons.
        /// </summary>
        /// <param name="ctrl">The control which invoked this event.</param>
        /// <param name="cancelDefault">
        /// False if the default behavior associated with the CommandBarButton control occurs, 
        /// unless it’s canceled by another process or add-in.
        /// </param>
        private void OnOntologyButtonClick(CommandBarButton ctrl, ref bool cancelDefault)
        {
            string[] parms = ctrl.Tag.Split(new string[] { Separator }, StringSplitOptions.None);

            try
            {
                if (parms != null)
                {
                    // Default term button which does not have a tagId.
                    if (parms.Length == 2 & parms[0] == this._defaultTermId)
                    {
                        this.ApplyMarkupToDefaultTerm();
                    }
                    else if (parms.Length == 3)
                    {
                        int tagId;

                        // Recognized terms contain a tagId.
                        RecognizedTermProperties props = this.TermCollection[parms[0]];

                        if (Int32.TryParse(parms[2], out tagId))
                        {
                            switch (tagId)
                            {
                                // Apply Same mark-up to all Instances of Term
                                case -2:
                                    this.ApplyMarkupToAllInstances(props.RecognizedTerm);
                                    break;

                                // View Term in OntologyBrowser
                                case -1:
                                    this.ViewInOntologyBrowser(props.RecognizedTerm);
                                    break;

                                // Add mark-up to the selected instance.
                                default:
                                    this.ApplyMarkupToTerm(props, tagId);
                                    break;
                            }
                        }
                    }
                }
            }
            finally
            {
                ContextMenuHelper.Instance().ClearBiolitMenus();
            }
        }

        /// <summary>
        /// Method to apply mark-up to the user selection, if it was not recognized as an Ontology term.
        /// User is prompted to choose the Ontology from the Ontology Browser.
        /// </summary>
        private void ApplyMarkupToDefaultTerm()
        {
            // Pop up the Ontology Browser for user to choose the Ontology term.
            MarkupAliasing markUpForm = new MarkupAliasing();
            DialogResult dialogResult = markUpForm.ShowDialog();
            if (dialogResult == DialogResult.OK)
            {
                // Apply mark-up to the selected text.
                WordCommonFuncs.OntologyMarkup(
                    this._defaultTermRange,
                    Convert.ToString(SmartTagHandlers.GetOntologyID(markUpForm.SelectedNode.OntologyName)),
                    markUpForm.SelectedNode.id,
                    markUpForm.SelectedNode.OntologyName,
                    markUpForm.SelectedNode.OntologyName);

                if (true == markUpForm.ApplyMarkupToAll)
                {
                    WordCommonFuncs.AddMarkupToAll(
                        this._defaultTermRange.Text.Trim(),
                        markUpForm.SelectedNode.id,
                        markUpForm.SelectedNode.OntologyName);
                }

                if (markUpForm.PersistTag == true)
                {
                    OntologyUtils.AddCustomTag(this._defaultTermRange.Text.Trim(), markUpForm.SelectedNode);
                }
            }
        }

        /// <summary>
        /// View the selected term in the Ontology Browser.
        /// </summary>
        /// <param name="ontologyTerm">The Ontology term to view in Browser.</param>
        private void ViewInOntologyBrowser(string ontologyTerm)
        {
            DocumentWrapper wordDocument = Globals.ThisAddIn.FindActiveWrapper();
            wordDocument.LookupTerm(ontologyTerm);
        }

        /// <summary>
        /// Apply Ontology Mark-up to all instances of the term.
        /// </summary>
        /// <param name="ontologyTerm">The term to add mark-up.</param>
        private void ApplyMarkupToAllInstances(string ontologyTerm)
        {
            OntologyCache.OntologyNode node = OntologyUtils.FindNthMatch(ontologyTerm);

            if (node != null)
            {
                WordCommonFuncs.AddMarkupToAll(ontologyTerm, node.id, node.OntologyName);
            }
        }

        /// <summary>
        /// Apply Ontology Mark-up to the selected term.
        /// </summary>
        /// <param name="termProperties">RecognizedTermProperties of the selected term.</param>
        /// <param name="tagId">The tagId of the Context Menu button control that was clicked.</param>
        private void ApplyMarkupToTerm(RecognizedTermProperties termProperties, int tagId)
        {
            OntologyCache.OntologyNode node = OntologyUtils.FindNthMatch(termProperties.TermRange.Text, tagId);

            if (node != null)
            {
                WordCommonFuncs.OntologyMarkup(
                    termProperties.TermRange,
                    Convert.ToString(SmartTagHandlers.GetOntologyID(node.OntologyName)),
                    node.id,
                    node.OntologyName,
                    node.OntologyName);
            }
        }
    }
}
